//
//  CJPlayer.swift
//  CJPack
//
//  Created by 陈警卫 on 2023-06-05.
//

import UIKit
import Foundation
import AVFoundation

class CJPlayer: NSObject {
    
    var didStatusAction: ((AVPlayerItem.Status) -> ())?
    var didLoadedTimeRangesAction: ((Double) -> ())? // 缓冲时长
    var didBufferEmptyAction: (() -> ())? // 正在缓存
    var didLikelyToKeepUpAction: (() -> ())? // 缓存可以继续播放
    var didPeriodicTimeAction: ((Double, Double, Double) -> ())? // 时间进度
    
    var didPlayPauseAction: ((Bool) -> ())? // 播放暂停
    
    var playerItem: AVPlayerItem?
    var player: AVPlayer?
    var playerLayer: AVPlayerLayer?
    var timeObserver: Any?
    var seekFinsih: Bool = true
    
    var timeString: String?
    var progress: Double?
    
    
    var isPlay: Bool = false {
        didSet {
            if let didPlayPauseAction = didPlayPauseAction {
                didPlayPauseAction(isPlay)
            }
        }
    }
    
    class func player(_ urlString: String) -> CJPlayer? {
        
        guard let url = URL(string: urlString) else { return nil }
        
        let player = CJPlayer()
        
        player.playerItem = AVPlayerItem(url: url)
        player.player = AVPlayer(playerItem: player.playerItem)
        
        player.playerLayer = AVPlayerLayer(player: player.player)
        player.playerLayer?.videoGravity = .resizeAspect
        
        player.setupObserver()
        
        return player
    }
    
    func play() {
        self.player?.play()
        self.isPlay = true
    }
    
    func pause() {
        self.player?.pause()
        self.isPlay = false
    }
    
    func setupObserver() {
        self.playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
        self.playerItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
        self.playerItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
        self.playerItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(playToEndTime),
                                               name: .AVPlayerItemDidPlayToEndTime, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive),
                                               name: UIApplication.willResignActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive),
                                               name: UIApplication.didBecomeActiveNotification, object: nil)
        setupTimeObserver()
    }
    
    func setupTimeObserver() {
        if let timeObserver = timeObserver {
            self.player?.removeTimeObserver(timeObserver)
        }
        self.timeObserver = self.player?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1), queue: DispatchQueue.main, using: { [weak self] time in
            self?.timeObserverAction(time: time)
        })
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if keyPath == "status" {
            if let status = self.playerItem?.status {
                if let didStatusAction = didStatusAction {
                    didStatusAction(status)
                }
            }
        } else if keyPath == "loadedTimeRanges" {
            // 缓冲时长
            let loadTimeRanges = self.playerItem?.loadedTimeRanges
            let newTimeRange = loadTimeRanges?.first as! CMTimeRange
            let start = CMTimeGetSeconds(newTimeRange.start)
            let duration = CMTimeGetSeconds(newTimeRange.duration)
            let totalBuffer = start + duration
            if let didLoadedTimeRangesAction = didLoadedTimeRangesAction {
                didLoadedTimeRangesAction(totalBuffer)
            }
        } else if keyPath == "playbackBufferEmpty" {
            // 正在缓存
            if let didBufferEmptyAction = didBufferEmptyAction {
                didBufferEmptyAction()
            }
        } else if keyPath == "playbackLikelyToKeepUp" {
            // 缓存可以继续播放
            if let didLikelyToKeepUpAction = didLikelyToKeepUpAction {
                didLikelyToKeepUpAction()
            }
        }
    }
    
    func timeObserverAction(time: CMTime) {
        let loadTime = time.seconds
        let totalTime = (self.player?.currentItem?.duration.seconds) ?? 0.0
        let progress = Double(loadTime/totalTime)
        self.progress = progress
        
        if let didPeriodicTimeAction = self.didPeriodicTimeAction,
           !totalTime.isNaN,
           !progress.isNaN,
           seekFinsih {
            didPeriodicTimeAction(loadTime, totalTime, progress)
        }
    }
    
    @objc func playToEndTime() {
        kCJLog("playToEndTime")
    }
    
    @objc func applicationWillResignActive() {
        self.pause()
    }
    
    @objc func applicationDidBecomeActive() {
        self.play()
    }
    
    deinit {
        if let timeObserver = timeObserver {
            self.player?.removeTimeObserver(timeObserver)
        }
        self.playerItem?.removeObserver(self, forKeyPath: "status")
        self.playerItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
        self.playerItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")
        self.playerItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
        NotificationCenter.default.removeObserver(self)
    }
    
}

extension CJPlayer {
    
    func setupSeek(progress: Double) {
        guard let currentTime = self.player?.currentItem?.currentTime().seconds,
              let totalTime = self.player?.currentItem?.duration.seconds,
              let timescale = self.player?.currentTime().timescale,
              !currentTime.isNaN && !totalTime.isNaN
        else {
            return
        }
        
        let time = CMTime(seconds: totalTime * progress, preferredTimescale: timescale)
        self.seekFinsih = false
        self.player?.seek(to: time, completionHandler: { [weak self] finish in
            self?.seekFinsih = true
        })
        self.setupTimeObserver()
    }
    
    
    func timeString(loadTime: Double, totalTime: Double) -> String {
        let totalM = Int(totalTime/60)
        let totalS = Int(totalTime.truncatingRemainder(dividingBy: 60))
        let currentM = Int(loadTime/60)
        let currentS = Int(loadTime.truncatingRemainder(dividingBy: 60))
        let timeString = String(format: "%.2i:%.2i/%.2i:%.2i", currentM, currentS, totalM, totalS)
        self.timeString = timeString
        return timeString
    }
}


enum CJPlayerOrientation: Int {
    case port
    case portfull
    case landfull
    case leftfull
    case rightfull
}

class CJPlayerConfig {
    
    var url: String?
    var orientation: CJPlayerOrientation = .port
}

class CJPlayerView: UIView {

    var config: CJPlayerConfig = CJPlayerConfig()
    
    var player: CJPlayer?
    
    var customView: UIView!
    var controlView: CJPControlView?
    
    var fullWindow: UIWindow?
    var portC: CJPFullController?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupView()
    }
    
    func setupView() {
        self.customView = UIView(frame: self.bounds)
        self.customView.backgroundColor = .black
        self.addSubview(self.customView)
    }
    
    func setupControl() {
        self.controlView?.removeFromSuperview()
        self.controlView = CJPControlView(frame: self.bounds)
        self.addSubview(self.controlView!)
        
        self.controlView?.player = self.player
        
        /// 全屏事件
        self.controlView?.didFullAction = { [weak self] in
            self?.actionFull()
        }
    }
    
    func setupConfig() {
        
        guard let urlString = self.config.url else { return }
        self.customView.cj_removeAllSubviews()
        self.customView.layer.cj_removeAllSublayers()
        
        self.player = CJPlayer.player(urlString)
        setAddLayer()
        
        self.player?.didStatusAction = { [weak self] status in
            if status == .readyToPlay {
                self?.playReadyToPlay()
            } else if status == .failed {
                self?.playFailed()
            }
        }
        
        self.player?.didPeriodicTimeAction = { [weak self] loadTime, totalTime, progress in
            self?.playPeriodicTime(loadTime: loadTime, totalTime: totalTime, progress: progress)
        }
        
        self.player?.didLoadedTimeRangesAction = { [weak self] timeRange in
            self?.playLoadedTimeRanges()
        }
        
        self.player?.didPlayPauseAction = { [weak self] isPlay in
            self?.playPause(isPlay)
        }
        
        setupControl()
    }
    
    func setAddLayer() {
        if let playerLayer = self.player?.playerLayer {
            playerLayer.frame = self.customView.bounds
            self.customView.layer.addSublayer(playerLayer)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        kCJLog("\(self)--deinit")
    }
    
}

extension CJPlayerView {
    
    // MARK: 全屏实现
    /// 全屏实现
    @objc func actionFull() {
        self.player?.playerLayer?.removeFromSuperlayer()
        
        self.portC = CJPFullController()
        self.portC?.player = self.player
        
        self.portC?.didDismissAction = { [weak self] in
            self?.fullWindow?.isHidden = true
            self?.setAddLayer()
        }
        
        self.fullWindow = UIWindow(frame: CJScreen.bounds)
        self.fullWindow?.windowLevel = .statusBar - 1
        self.fullWindow?.rootViewController = self.portC
        self.fullWindow?.backgroundColor = .black
        self.fullWindow?.isHidden = false
    }
}

extension CJPlayerView {
    
    func playReadyToPlay() {
        self.player?.play()
    }
    
    func playFailed() {
        
    }
    
    func playLoadedTimeRanges() {
        
    }
    
    func playPeriodicTime(loadTime: Double, totalTime: Double, progress: Double) {
        
        let timeString = self.player?.timeString(loadTime: loadTime, totalTime: totalTime)

        self.controlView?.time = timeString
        self.controlView?.progress = progress
        self.portC?.controlView.time = timeString
        self.portC?.controlView.progress = progress
    }
    
    func playPause(_ isPlay: Bool) {
        self.controlView?.isPlay = isPlay
        self.portC?.controlView.isPlay = isPlay
    }
    
}
